package edu.siu.math.egut.main;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;

import org.apache.commons.collections15.list.TreeList;

import edu.siu.math.egut.io.Algorithms;
import edu.siu.math.egut.util.Term;
import edu.siu.math.egut.util.Piece;
import edu.siu.math.egut.util.LaurentPolynomial;
import edu.siu.math.egut.util.LaurentPolynomialReadException;
import edu.siu.math.egut.util.OneParameterSubGroup;
import edu.siu.math.egut.util.ExceptionalGroupElement;

/**
 * Facilitates computations of the unramified vector in an induced 
 * representation over a unipotent group against a character or 
 * more general "psi factor."  
 * 
 * @author josephhundley
 *
 */


public class SectionIntegral {

    
    private static List<String> pieces = new TreeList<String>();
    private static int rank = 1;
    private static String currentPiece;
    // TODO learn how to have it write files
    // learn how to make it spawn directories to keep 
    // all the serialized pieces, tex file for 
    // finished pieces, and the piece relationship file.
    // add a file detailing relationships between pieces.
    // add files for finished pieces.
   

    /**
     * @param args ignore
     */
    public static void main(String[] args) {

	//Get the initial data.
/*	System.out.println("Input the name of a file containing the desired data in the following order:\n " +
			"\t left invariance roots (P . w)" +
			"\t roots defining the group the integral is over (N)");
*/	
	Scanner scanner = new Scanner(System.in);
	String filename = "TestSectInt";// edu.siu.math.egut.io.Algorithms.getFileName(scanner);
	File file = new File(filename);
	
	String s;
	try {
	    s = Algorithms.fileToString(filename);
	} catch (FileNotFoundException e) {
	    s="[[1]],[[1]]";  // to ensure that s is always initialized before its use later... though this 
	    // line would never be read...
	}	int[][] leftInv=new int[1][1];
	int[][] m = new int[1][1];
	try {
	    leftInv = Algorithms.readLiEMatrix(s);

	    m = Algorithms.readLiEMatrix(s, s.indexOf("]]")+2);
	} catch (Exception e) {
	    System.out.println("Problem reading file.");
	    e.printStackTrace();
	    System.exit(1);
	}
	// initialize the constant "rank"
	rank =m[0].length;
	
	
	// set up the unipotent 
	ExceptionalGroupElement u = new ExceptionalGroupElement(m, "u");
	
	// show the user the unipotent 
	System.out.println(u);
	
	// get a polynomial (the psi factor)
/*	System.out
		.println("Next, please input a polynomial in the variables corresponding to the roots "
			+ "from the second matrix as above.");
*/
	LaurentPolynomial p = LaurentPolynomial.create("u1");//edu.siu.math.egut.io.Algorithms.getPoly(scanner);
	
/*	System.out.println("Finally, please input a name for this \n" +
			"integral.  (This should be a string.  When pieces\n" +
			"of the integral are stored, the filenames will consist" +
			"\n of this name and a numerical suffix.)");
*/	String name = "testIntegral"; //scanner.next();
	
	pieces.add(name);
	currentPiece = name;
	Piece thisPiece =  new Piece(leftInv, null , u, p);
	Piece.savePiece(currentPiece, thisPiece);

	
	boolean done = false;
	while( !done)
	{
	    // Show the user the unipotent, and psi factor: 
	    System.out.println(u+"\n psi("+p+")\n\n");
	    // show the user his/her options
	    System.out.println("I = split into two pieces using Iwasawa decomposition on rightmost root.\n"
			    + "R = re-order the Sunipotents\n"
			    + "C = conjugate a Sunipotent from a specified root from right to left\n"
			    + "S = make a substitution\n"
			    + "N = done with this piece, load next\n"
			    + "D=  done with this piece, load selected\n"
			    + "P = switch to another piece without marking this one \"Done\"\n"
			    + "Q = Quit\n" + "");
	    char input;
	    String inputString;
	    boolean gotInput = false;
	    while (!gotInput) {
		gotInput = true;
		
		do{
		inputString  = scanner.nextLine().trim();
		}while(! (inputString.length() >0));
		input = inputString.charAt(0);
		if (input == 'I'|| input == 'i')// do Iwasawa on rightmost
		{
		    System.out.print("Iwasawa...\n");
		    // TODO add checking:  can't Iwasawa on x_a(r) unless -a is in the invRoots.
		    if( u.getLast().getPolynomial().getDegree()!= 1|| u.getLast().getPolynomial().getNumberOfTerms()!=1)
			System.out.println("The rightmost Sunipotent has a complicated argument.  This program\n" +
					"can not handle Iwasawa for such a situation (sorry).  \n" +
					"Try reorder or substitute.\n\n");
		    else
		    {
			// identify the (unique) variable appearing in that last polynomial
			String variable = u.getLast().getPolynomial().getVar();
			
			// determine whether it appears in the psi factor and print alert if it does.
			if(!p.isIndependentOf(variable))
			    System.out.println("Alert.  Iwasawa on a variable which appears in the psi factor.\n");
			// determine whether it appears in any terms other than the last and print alert if it does
			for( int i=u.getNumberOfTerms() -2; i >-1 ; i--){
			    if(!u.get(i).getPolynomial().isIndependentOf(variable))
				System.out.print("Alert.  Iwasawa variable also appears in term "+ i + ":  "+u.get(i) + "\n");
			    
			}
			// if variable was associated to a different Suni, print alert.
			if(u.suniOf(variable) != u.getLast())
			    System.out.println("Alert.  Iwasawa variable was associated to another Sunipotent.  Association deleted.\n");
			// in any event, delete any associations involving this variable.
			u.disassociate(variable);
			    
			
			
			
			// manage the Piece list "pieces."
			int index = pieces.indexOf(currentPiece);
			pieces.remove(index);
			pieces.add(index, currentPiece+"0");
			pieces.add(index, currentPiece+"1");
			

			// grab the root from the last Suni before removing it.
			int[] a = u.getLast().getRoot();
			
			

			
			// save first piece:  variable is in \frak{o}:  simply drop the last term.
			u.remove(u.getNumberOfTerms()-1);
			
			// we allowed the variable to still appear in the torus piece, but in that
			//case it should be dropped from there.
			if( thisPiece.isIndependentOf(variable)){
			    for( int i = 0 ; i < rank ; i++ )
				thisPiece.setTorusPart(i, thisPiece.getTorusPart(i).replace(variable, LaurentPolynomial.create(1)));
			}
			
			// TODO assess whether variable still exists anywhere.  places to check 
			// are the current domain of integration, the psi Factor, the remaining Sunis.
			// if it doesn't drop the new Piece has the "same" domain of integration 
			// as the old one. (one less variable, no more constraints.) 
			// if the variable still exists, then constraint must be added to domain
			
		
			
			Piece.savePiece( currentPiece+"0", new Piece(leftInv, null, u , p, 
				thisPiece.getTorusPart(), thisPiece.getJacobian()));

			// handle conjugation by the torus part from the Iwasawa
			for( OneParameterSubGroup suni :u.getTerms()){
			    
			    // rule: x_r(p) . av( t^{-1} ) = av(t^{-1}) . x_r( p * t^{<r, av>})
			    // here av = coroot for root a, < , >  = natural pairing
			    int [] r = suni.getRoot();
			    suni.setPolynomial(suni.getPolynomial().times(Term.create(variable, 
				    2*u.getGroup().weylInvtInnProd(r, a)/u.getGroup().weylInvtInnProd(a, a))));
			    // <r, av> = 2(r,a)/(a,a) where (,) = Weyl invariant inner product.
			    
			    if( u.stringOf(suni)!= null){
				// make changes of variable to simplify those sunis that are
				// associated to certain variables:  y ->y *t^{-<, av>} (notation as above.)
				// if previous step changed x_r(y) to x_r( y* t^{<r, av>}), this changes it back.
				u.replace(u.stringOf(suni), LaurentPolynomial.create(u.stringOf(suni)).times(
					Term.create(variable, 
					    -2*u.getGroup().weylInvtInnProd(r, a)/u.getGroup().weylInvtInnProd(a, a))
					));
				p.replace(u.stringOf(suni), LaurentPolynomial.create(u.stringOf(suni)).times(
					Term.create(variable, 
					    -2*u.getGroup().weylInvtInnProd(r, a)/u.getGroup().weylInvtInnProd(a, a))
					));
			    }// end if stringOf != null
			    
			    
			}// end iteration over sunis 
			
			// introduce the upper unipotent part of the Iwasawa at right
			// new root will be negative of root from original suni.
			for( int i = 0 ; i < a.length; i++)
			    a[i] *= -1;
			u.rightMultiply(OneParameterSubGroup.create(a, LaurentPolynomial.create(variable)));
			// FIXME if a is not actually in leftInv, this next step is simply wrong.
			u.pushFarLeftAndRemove(u.getNumberOfTerms()-1);
			u = edu.siu.math.egut.egut.Algorithms.l_reduce(u, leftInv);
			p= u.chOfVars(p);
			currentPiece = currentPiece +"1";
			
			//TODO have it check whether negative exponents remain and print a warning if they do.
			
			
			
			
			
		    }		}
		else if (input == 'R'|| input== 'r')// reorder
		{
		    
		    
		    boolean doneReordering = false;
		    while (!doneReordering){
			System.out.print(u.toStringWithIndices());
			System.out.println("\n Which simple unipotent do you wish to move?  (Input -1 when done reordering.)");
			int i = scanner.nextInt();
			
			if (i < 0 || i > u.getNumberOfTerms())
			    doneReordering = true;
			else {
			    System.out
				    .println("\n Where do you wish to move it? (Input -1 to exit reordering.)");
			    int j = scanner.nextInt();
				
			    if (j < 0 || j > u.getNumberOfTerms())
				doneReordering = true;
			    else
			    {
				u.move(i, j);
				u.cleanUp();
			    }
			    
			}
			
		    }
		    System.out.println("Done reordering.");

		} else if (input == 'c'|| input =='C')// conjugate
		{
		    
		    // Get a root. 
		    System.out.println("Input a root (as a "+ u.getGroup().getRank()+" digit integer).\n");
		    int [] root = edu.siu.math.egut.io.Algorithms.getRoot(scanner, u.getGroup());
		    
		    
		    ExceptionalGroupElement u1 = ExceptionalGroupElement.times(u, OneParameterSubGroup.create(root, LaurentPolynomial.create("z"))); 
		    
		    // TODO If/when the possibility to customize variable names is added, the user 
		    // will need to be warned not to use z, and/or given the ability to change the 
		    // variable which is in this role.
		    
		    u1.pushFarLeftAndRemove(u1.getNumberOfTerms()-1);
		    u1 = edu.siu.math.egut.egut.Algorithms.l_reduce(u1, leftInv);
		    LaurentPolynomial p1 = u1.chOfVars(p); // this will make the COV's in u and in p.
		    
		    
		    if( u1.equals(u))
			System.out.println(p1+"\n\n");
		    else 
			System.out.println("Conjugation failed.  This would occur, e.g., if the one-parameter subgroup\n " +
					"corresponding to the  " +
					"root entered does not normalize the unipotent group under consideration.\n");
			
		    
		    
		    
		    
		    

		} else if (input == 'S' || input == 's')// substitution
		{
		    // TODO add some verification?
		    
		      //get variable 
		    System.out.println("Input a variable.");
		    String variable = scanner.next();
		    
		    // get polynomial
		    System.out.println("Input the polynomial you wish to substitute for this variable.");
		    LaurentPolynomial q = edu.siu.math.egut.io.Algorithms.getPoly(scanner);
		    
		    p.replace(variable, q);
		    u.replace(variable, q);
		     
		     

		} else if (input == 'N' || input=='n')// done move on to next piece
		{
		    // write current piece in final form
		    Piece.savePiece(currentPiece, thisPiece);
		    
		    // store location. 
		    int index = pieces.indexOf(currentPiece);
		    
		    // current piece is finished. delete it from the list.
		    pieces.remove(currentPiece);
		    
		    // figure out what is "next."
		    if( pieces.size() != 0 ){
			if (index < pieces.size()) 
			    currentPiece = pieces.get(index);
			else 
			    currentPiece = pieces.get(0);
			thisPiece = Piece.loadPiece(currentPiece);
		    }// end if pieces size != 0...
		    else // we're done!
		    {
			System.out.println("Integral completed!\n");
			done = true;
			
		    }

		} else if (input == 'd'|| input == 'D') // done move on to selected piece
		{
		    // TODO implement go on to selected piece
		    // write current piece in final form
		    Piece.savePiece(currentPiece, thisPiece);
		    // current piece is finished. delete it from the list.
		    pieces.remove(currentPiece);
		    
		    if( pieces.size()== 0 ){
			System.out.println("\nCongratulations, you're done.\n");
			done = true;
		    }
		    else{
		    System.out.println("Here is the current Piece list"+pieces+"\n\n Input your selection (Suffix only.)");
		    	currentPiece = edu.siu.math.egut.io.Algorithms.getPieceName(scanner, name, pieces);
		    	
		    	// will be null if user has bailed on inputing name.
		    	if( currentPiece != null ){
		    	    thisPiece = Piece.loadPiece(currentPiece);		    	    
		    	}
		    }

		} else if (input == 'p'|| input == 'P')// switch piece w/o marking this one
					// done
		{
		    
		    System.out.println("This option is not available yet.");
		    
		    //System.out.println("Save current piece?");
		    // TODO get response act accordingly.
		    
		    // TODO get piece to switch to. load it.
		    

		} else if (input == 'q'|| input == 'Q')// quit
		{
		    //check if user wants to update the stored version of the current piece. 
		    //System.out.println("Save current piece before quitting?");
		 //TODO if so, do so. 
		    done = true;
		     
		} else {
		    System.out.print("Problem reading input.  Please try again.");
		    gotInput = false;
		}

	    }// end while not got the input
	   
	 
	    
	}// end while not done

	
    }// end main

}// end class declaration
